// ==UserScript==
// @name         吉时学助手
// @namespace    https://github.com/cnzxo
// @version      1.0.17
// @description  吉时学在线课程学习助手，帮助用户更好的完成学习和考试任务。
// @author       www@cnzxo.com
// @match        https://elearning.geely.com/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=geely.com
// @grant        none
// @license      GPL-3.0-only
// @copyright    2025, https://github.com/cnzxo
// ==/UserScript==

(async function () {
    'use strict';

    if (document.documentElement.dataset.__geely_study_running__ === "true") return;
    document.documentElement.dataset.__geely_study_running__ = "true";

    const G_ROOT_URL = window.location.origin + window.location.pathname;
    const G_EXAM_LIST_URL = `${G_ROOT_URL}gke/user/exam/list`;
    const G_EXAM_VIEW_URL = `${G_ROOT_URL}#/user/examview`;
    const G_EXAM_INFO_URL = `${G_ROOT_URL}gke/user/exam/info`;
    const G_COURSE_LIST_URL = `${G_ROOT_URL}gke/user/course/usercenter/coursepushs`;
    const G_QUERY_COURSE_URL = `${G_ROOT_URL}gke/user/course/olconfig/query`;
    const G_SEARCH_COURSE_URL = `${G_ROOT_URL}gke/user/course/learning/detail/search`;
    const G_GEELY_API_URL = `https://0a774aa6.nip.io/`;
    const G_READY_QUEUE_SIZE = 100;
    const G_START_QUEUE_SIZE = 5;
    let taskReadyQueue = [];
    let taskStartQueue = [];

    async function searchCouseDetail(courseId, wareId) {
        return await fetch(G_SEARCH_COURSE_URL, {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: new URLSearchParams({ courseId: courseId, wareId: wareId }),
        }).then(res => res.json());
    }

    async function addTimerTask(task) {
        if (taskReadyQueue.length >= G_READY_QUEUE_SIZE) return;
        if (taskReadyQueue.filter(t => t.courseId === task.courseId && t.wareId == task.wareId).length > 0) return;
        taskReadyQueue.push(task);
    }

    function convertAnswer(type, item) {
        if (type === 1) {
            if (item === "1") return "对";
            if (item === "2") return "错";
        }
        return item;
    }

    async function updateExamList(data) {
        const interval = setInterval(async () => {
            const trNodes = document.querySelectorAll('#user-center-exam-2 > div.exam-type.exam-type-2 > ul > li > table > tbody > tr');
            if (!trNodes || trNodes.length === 0) return;
            await Promise.all(
                Array.from(trNodes).map(async (trNode) => {
                    const tdNode1 = trNode.querySelector('td.singleLineName');
                    const tdNode2 = trNode.querySelector('td > span.color-grey');
                    if (tdNode1 && tdNode2 && tdNode2.textContent.includes("不能查看")) {
                        const course = data.find(i => i.name == tdNode1.textContent);
                        if (!course) return;
                        tdNode2.innerHTML = `<a href="${course.url}"><i class="ion-android-arrow-dropright-circle mr8"></i><time data-i18n="public_view">查看</time></a>`;
                    }
                })
            );
            clearInterval(interval);
        }, 500);
        setTimeout(() => clearInterval(interval), 5000);
    }

    async function requestExamList(pageNum) {
        await fetch(`${G_EXAM_LIST_URL}?pageNum=${pageNum}&pageSize=10&examStatus=2&flag=&_=${Date.now()}`).then(async r => r.json()).then(async r => {
            const data = [];
            if (!r.data) return;
            for (const item of r.data.items) {
                if (item.resultFlag === 1) {
                    data.push({
                        id: item.id,
                        name: item.examName,
                        url: G_EXAM_VIEW_URL + "/" + item.id,
                    });
                }
            }
            await updateExamList(data);
        });
    }

    async function waitForLoad() {
        const interval = setInterval(async () => {
            const activeTab = document.querySelector('.tab.active a');
            if (activeTab?.hash.includes("#user-center-exam-2") && !activeTab.dataset.flag) {
                activeTab.dataset.flag = true;
                activeTab.addEventListener("click", async () => {
                    await requestExamList(1);
                    await waitForLoad();
                });
            }
            const pages = document.querySelectorAll('li.page-number');
            if (!pages || pages.length === 0) return;
            await Promise.all(
                Array.from(pages).map(item => {
                    if (item.className.includes("active") || item.className.includes("disabled") || item.dataset.flag === true) return;
                    item.dataset.flag = true;
                    item.addEventListener('click', async () => {
                        const currentPage = document.querySelector("li.page-number.active");
                        if (!currentPage) return;
                        await requestExamList(parseInt(currentPage.textContent.trim()));
                        await waitForLoad();
                    });
                })
            );
            clearInterval(interval);
        }, 500);
        setTimeout(() => clearInterval(interval), 5000);
    }

    async function checkNetworkStatus() {
        if (await fetch(G_GEELY_API_URL, { method: 'HEAD', mode: 'no-cors' }).then(async () => true).catch(async () => false) === true) return true;
        alert("请点击新页面中的：高级 >> 继续访问");
        window.open(G_GEELY_API_URL);
        return false;
    }

    async function showExamAnswer(examId, data) {
        if (document.querySelector('div[type="dialog"]')) {
            alert('❌请点击开始考试!!!');
            return;
        }
        if (Object.keys(data).length === 0) {
            if (await checkNetworkStatus() === false) return;
            data = await fetch(`${G_GEELY_API_URL}exam/query/?examId=${examId}`).then(res => res.json()).catch((e) => { return {}; });
            if (Object.keys(data).length === 0 || !data.questions || data.questions.length === 0) {
                alert('❌暂无答案!!!');
                return;
            }
        }
        data.questions.forEach(item => {
            if (!item.questionId) item.questionId = item.id;
            const questionNode = document.querySelector(`input[name="${item.questionId}"]`);
            if (!questionNode) return;
            const examItemNode = questionNode.closest('.exam-item');
            if (!examItemNode) return;
            const titleNode = examItemNode.querySelector('.text-bold > td');
            if (!titleNode) return;
            titleNode.querySelector('b')?.remove();
            titleNode.innerHTML = titleNode.innerHTML + `<b style="color: green;margin-left: 10px;">${convertAnswer(item.questionType, item.answer)}</b>`
        });
    }

    async function addExamAnswerEvent(examId, data = {}) {
        const interval = setInterval(() => {
            const fbDom = document.getElementById("feedback");
            if (!fbDom) return;
            const liDom = document.createElement('li');
            liDom.id = "func-menu";
            const openExamAnswer = showExamAnswer.bind(null, examId, data);
            liDom.addEventListener("click", openExamAnswer);
            liDom.innerHTML = `<i class="iconfont ion-icecream"></i><div class="feedback-title"><time data-i18n="查看答案">查看答案</time><span></span></div>`;
            fbDom.appendChild(liDom);
            clearInterval(interval);
        }, 500);
        setTimeout(() => clearInterval(interval), 5000);
    }

    async function requestExamDetail(id) {
        await fetch(`${G_EXAM_INFO_URL}?id=${id}&_=${Date.now()}`).then(async res => res.json()).then(async res => {
            await addExamAnswerEvent(res.data.exam.id, res.data);
        });
    }

    async function updateExamAnswer(id) {
        await fetch(`${G_EXAM_INFO_URL}?id=${id}&_=${Date.now()}`).then(async res => res.json()).then(async res => {
            if (await checkNetworkStatus() === false) return;
            await fetch(`${G_GEELY_API_URL}exam/add/`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(res.data),
            });
        });
    }

    function onClickSubmitExam() {
        const matchOnExam = window.location.hash.match(/\#\/user\/onexam\/(\d+)\/(\d+)/);
        if (matchOnExam) setTimeout(() => updateExamAnswer(matchOnExam[2]), 1000);
    }

    async function addExamSubmitEvent() {
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType !== 1) return;
                    const btnNode = node.querySelector('a.layui-layer-btn0');
                    if (node.tagName === 'DIV' && btnNode && btnNode.textContent === '确定' && btnNode.dataset.flag !== true) {
                        btnNode.dataset.flag = true;
                        btnNode.addEventListener('click', onClickSubmitExam);
                    }
                });
            });
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    async function queryCourseDetail(courseId) {
        return await fetch(`${G_QUERY_COURSE_URL}?id=${courseId}&_=${Date.now()}`).then(async res => res.json()).then(async res => {
            let pendingCourseCount = 0;
            for (const topic of res.data.courseOlContentList) {
                for (const course of topic.list) {
                    if (course.completeState === 2) continue;
                    pendingCourseCount++;
                    const url = `${G_ROOT_URL}#/watchcourse/${courseId}/${course.fileId}`;
                    await addTimerTask({ courseId: courseId, wareId: course.wareId, url: url });
                }
            }
            return pendingCourseCount;
        });
    }

    async function startLearnCourse() {
        const navBar = document.querySelector('.nav-bar.nav-simple > ul > li.active');
        if (!navBar || navBar.dataset.target !== 'code-4') return;
        const activeTab = document.querySelector(`#${navBar.dataset.target} li.page-number.active`);
        if (!activeTab) return;
        let pendingCourseCount = 0;
        await fetch(`${G_COURSE_LIST_URL}?pageNum=${activeTab.textContent.trim()}&pageSize=6&_=${Date.now()}`).then(async res => res.json()).then(async res => {
            if (!res.data) return pendingCourseCount;
            for (const topic of res.data.items) {
                if (!topic.list) continue;
                for (const course of topic.list) {
                    if (course.completeState === 2) continue;
                    pendingCourseCount += await queryCourseDetail(course.courseId);
                }
            }
            return pendingCourseCount;
        });
        if (pendingCourseCount === 0) alert('✅暂无未完成的课程!!!');
    }

    async function addStartLearnBtn() {
        const interval = setInterval(() => {
            const fbDom = document.getElementById("feedback");
            if (!fbDom) return;
            const liDom = document.createElement('li');
            liDom.id = "func-menu";
            liDom.addEventListener("click", startLearnCourse);
            liDom.innerHTML = `<i class="iconfont ion-icecream"></i><div class="feedback-title"><time data-i18n="开始学习">开始学习</time><span></span></div>`;
            fbDom.appendChild(liDom);
            clearInterval(interval);
        }, 500);
        setTimeout(() => clearInterval(interval), 5000);
    }

    async function playCourseVideo() {
        const interval = setInterval(() => {
            const video = document.querySelector('#content-view > video');
            const playBtn = document.querySelector('.prism-big-play-btn');
            if (!video || !playBtn) return;
            setTimeout(() => {
                video.muted = true;
                video.autoplay = true;
                video.playsinline = true;
                video.webkitPlaysInline = true;
                video.playbackRate = 2.0;
                playBtn.click();
            }, 1000);
            clearInterval(interval);
        }, 500);
        setTimeout(() => clearInterval(interval), 5000);
    }

    async function onPageLoad() {
        const hash = window.location.hash;
        document.getElementById("func-menu")?.remove();
        if (hash.includes("#/user/center/exam") || hash.includes("#/user/center/exam/complete") || hash.includes("#user-center-exam-2")) {
            await requestExamList(1);
            await waitForLoad();
            return;
        }
        const matchExamDetail = hash.match(/\#\/user\/examview\/(\d+)/);
        if (matchExamDetail) {
            await requestExamDetail(matchExamDetail[1]);
            return;
        }
        const matchOnExam = window.location.hash.match(/\#\/user\/onexam\/(\d+).*?/);
        if (matchOnExam) {
            await addExamSubmitEvent();
            await addExamAnswerEvent(matchOnExam[1]);
            return;
        }
        const matchUserCourse = hash.match(/\#\/user\/center\/course/);
        if (matchUserCourse) {
            await addStartLearnBtn();
            return;
        }
        const matchWatchCourse = hash.match(/\#\/watchcourse\/(\d+)\/(\d+)/);
        if (matchWatchCourse) {
            await playCourseVideo(matchWatchCourse[1], matchWatchCourse[2]);
            return;
        }
    }

    window.addEventListener('hashchange', onPageLoad);
    window.addEventListener("load", onPageLoad);

    async function doTimerTask() {
        console.log('doTimerTask, start queue size: ', taskStartQueue.length, 'ready queue size: ', taskReadyQueue.length);
        for (let i = 0; i < taskStartQueue.length; i++) {
            const task = taskStartQueue.shift();
            const res = await searchCouseDetail(task.courseId, task.wareId);
            if (res.data.completeState === 2) continue;
            taskStartQueue.push(task);
        }
        for (let i = 0; i < taskReadyQueue.length; i++) {
            if (taskStartQueue.length >= G_START_QUEUE_SIZE) break;
            const task = taskReadyQueue.shift();
            const res = await searchCouseDetail(task.courseId, task.wareId);
            if (res.data.completeState === 2) continue;
            if (taskStartQueue.length < G_START_QUEUE_SIZE) {
                if (taskStartQueue.filter(t => t.courseId === task.courseId && t.wareId == task.wareId).length > 0) continue;
                taskStartQueue.push(task);
                window.open(task.url, '_blank');
                await new Promise(resolve => setTimeout(resolve, 1000));
                continue;
            }
            taskReadyQueue.push(task);
        }
    }
    setInterval(() => doTimerTask(), 60000);
})();